package org.dreamwork.jasmine2.editor.ckeditor.controls;

import org.dreamwork.jasmine2.engine.IPostbackHandler;
import org.dreamwork.jasmine2.events.EventException;
import org.dreamwork.jasmine2.web.IWebContainer;
import org.dreamwork.jasmine2.web.controls.InternalJS;
import org.dreamwork.jasmine2.web.controls.Literal;
import org.dreamwork.jasmine2.web.controls.TextBox;
import org.dreamwork.jasmine2.web.controls.WebContainer;
import org.dreamwork.peony.DataBinder;
import org.dreamwork.util.NameValueCollection;
import org.dreamwork.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 12-7-2
 * Time: 下午4:49
 */
public class CKEditorControl extends WebContainer implements IPostbackHandler {
    private static String root;
    private static final String BASE_SCRIPT_KEY = "ckeditor.script";
    private static final String BASE_SCRIPT_SRC = "ckeditor/ckeditor.js";
/*
    private static final String UPLOAD_SCRIPT_SRC = "org/dreamwork/jasmine2/editor/ckeditor/resource/plugins/upload/upload-dialog.js";
    private static final String PARAMETERS_SCRIPT_SRC = "org/dreamwork/jasmine2/editor/browser/resource/script/Parameter.js";
*/
    private static final String UPLOAD_SCRIPT_SRC = "ckeditor/plugins/upload/upload-dialog.js";
    private static final String PARAMETERS_SCRIPT_SRC = "browser/resource/script/Parameter.js";

    private String base;
    private String html;
    private Integer width, height;
    private String theme, skin;
    private boolean escapeHtmlChar = false, autoUpdateElement = true;
    private String toolbar;
    private String domain;
    private boolean changed = false;

    private static DocumentBuilder builder;

    static {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance ();
            builder = factory.newDocumentBuilder ();
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }
    }

    public CKEditorControl () {
        htmlTagName = "";
    }

    public String getBase () {
        return base;
    }

    public void setBase (String base) {
        this.base = base;
    }

    public String getHtml () {
        return html;
    }

    public void setHtml (String html) {
        this.html = html;
        if (viewStateEnabled) {
            if (html != null)
                stateManager.put ("html", html);
            else
                stateManager.remove ("html");
        }
    }

    public String getSkin () {
        return skin;
    }

    public void setSkin (String skin) {
        this.skin = skin;
    }

    public Integer getWidth () {
        return width;
    }

    public void setWidth (Integer width) {
        this.width = width;
    }

    public Integer getHeight () {
        return height;
    }

    public void setHeight (Integer height) {
        this.height = height;
    }

    public String getTheme () {
        return theme;
    }

    public void setTheme (String theme) {
        this.theme = theme;
    }

    public boolean isEscapeHtmlChar () {
        return escapeHtmlChar;
    }

    public void setEscapeHtmlChar (boolean escapeHtmlChar) {
        this.escapeHtmlChar = escapeHtmlChar;
    }

    public boolean isAutoUpdateElement () {
        return autoUpdateElement;
    }

    public void setAutoUpdateElement (boolean autoUpdateElement) {
        this.autoUpdateElement = autoUpdateElement;
    }

    public String getToolbar () {
        return toolbar;
    }

    public void setToolbar (String toolbar) {
        if (StringUtil.isEmpty (toolbar)) this.toolbar = null;
        if (toolbar.startsWith ("[["))
            toolbar = toolbar.substring (1);
        if (toolbar.endsWith ("]]"))
            toolbar = toolbar.substring (0, toolbar.length () - 1);
        this.toolbar = toolbar;
    }

    public String getDomain () {
        return domain;
    }

    public void setDomain (String domain) {
        this.domain = domain;
    }

    public Document getContentAsDom () throws IOException, SAXException {
        if (StringUtil.isEmpty (html)) return null;

        if (builder == null) return null;

        String content = "<div>" + html.replaceAll ("&(.*?);", "") + "</div>";
        ByteArrayInputStream in = new ByteArrayInputStream (content.getBytes ("utf-8"));
        return builder.parse (in);
    }

    public List<String> getImageSources () {
        try {
            root = context.getRequest ().getContextPath ();
            if (StringUtil.isEmpty (root)) root = "/";
            else if (root.endsWith ("/")) root = root.substring (0, root.length () - 1);
            Document doc = getContentAsDom ();
            List<String> list = new ArrayList<String> ();
            if (doc != null) {
                NodeList nodes = doc.getElementsByTagName ("img");
                for (int i = 0; i < nodes.getLength (); i ++) {
                    Element e = (Element) nodes.item (i);
                    if (e.hasAttribute ("src")) {
                        String src = e.getAttribute ("src");
                        if (src.startsWith (root))
                            src = src.substring (root.length ());
                        list.add (src);
                    }
                }
            }
            return list;
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }
    }

    @Override
    public void init () {
        super.init ();
        HttpServletRequest request = context.getRequest ();
        if (root == null) {
            root = request.getContextPath ();
        }
        if (!StringUtil.isEmpty (domain)) {
            page.addInitStatement ("domainName", String.format ("var __domain_name = '%s'", domain));
        }
        page.addInitStatement ("editor_path", String.format ("var CONTEXT_PATH = '%s'", root));
        page.addStartupStatement ("__on_page_loaded ();");
        page.registerClientScriptBlock (InternalJS.class, InternalJS.EVENT_JS);
//        page.registerClientScriptBlock (getClass (), UPLOAD_SCRIPT_SRC);
//        page.registerClientScriptBlock (getClass (), PARAMETERS_SCRIPT_SRC);
        if (context.getAttribute (BASE_SCRIPT_KEY) == null) {
            IWebContainer container = page.getHead();
            if (container == null) container = page.getBody();
            if (container == null) container = page.getForm();
            if (container == null) container = page.getHtml ();
            if (container == null) container = page;

            container.addControl (new Literal (String.format ("<script type='text/javascript' src='%s/%s'></script>%n", root, BASE_SCRIPT_SRC)));
            container.addControl (new Literal (String.format ("<script type='text/javascript' src='%s/%s'></script>%n", root, UPLOAD_SCRIPT_SRC)));
            container.addControl (new Literal (String.format ("<script type='text/javascript' src='%s/%s'></script>%n", root, PARAMETERS_SCRIPT_SRC)));
            context.setAttribute (BASE_SCRIPT_KEY, true);
        }
    }

    public void postbackData (NameValueCollection props) {
        html = props.getValue (id);
        if (stateManager != null) {
            String viewState = (String) stateManager.get ("text");
            changed = html != null && !html.equals (viewState);
        }
    }

    @Override
    public void onDataBind (Object sender) throws EventException {
        super.onDataBind (sender);
        if (dataSource != null && !StringUtil.isEmpty (valueField)) {
            Object value = DataBinder.eval (dataSource, valueField);
            if (value == null) value = "";
            if (!changed)
                setHtml (String.valueOf (value));
        }
    }

    @Override
    public void createChildControls () {
        TextBox box = new TextBox ();
        box.setViewStateEnabled (false);
        box.setMultiline (true);
        box.setId (id);
        if (!StringUtil.isEmpty (html))
            box.setText (escapeHtmlChar ? escapeHTML (html) : html);
        addControl (box);

        createJavascript ();

        childControlCreated = true;
    }

    private void createJavascript () {
        String script =
                "<script type=\"text/javascript\">" +
                "var " + id + " = CKEDITOR.replace( '" + id + "', {";
        if (StringUtil.isEmpty (toolbar))
            script += "toolbar: CK_EDITOR_TOOLBAR";
        else
            script += "toolbar: [" + toolbar + ']';
        if (width != null && width != 0) script += ",width:'" + width + "'";
        if (height != null && height != 0) script += ",height:'" + height + "'";
        if (!StringUtil.isEmpty (theme)) script += ",theme:'" + theme + "'";
        if (!StringUtil.isEmpty (skin)) script += ",skin:'" + skin + "'";
        if (!autoUpdateElement) script += ",autoUpdateElement:false";
        script += "});\r\n" +
                  "__rte_editors.push ('" + id + "');\r\n" +
                  "</script>";
        addControl (new Literal (script));
    }

    private String escapeHTML (String html) {
        return html.replace ("&", "&amp;").replace ("<", "&lt;").replace (">", "&gt;");
    }
}